﻿using System;
using System.Collections.Generic;
using System.IO;
using fastCSharp.reflection;
using fastCSharp.io;

namespace fastCSharp.code
{
    /// <summary>
    /// CSharp代码生成器
    /// </summary>
    internal sealed class coder : template<treeBuilder.node>
    {
        /// <summary>
        /// 模板文件路径
        /// </summary>
        private static readonly string templatePath = (@"ui\code\cSharp\template\").pathSeparator();
        /// <summary>
        /// 模板命令类型
        /// </summary>
        internal enum command
        {
            /// <summary>
            /// 输出绑定的数据
            /// </summary>
            AT,
            /// <summary>
            /// 子代码段
            /// </summary>
            PUSH,
            /// <summary>
            /// 循环
            /// </summary>
            LOOP,
            /// <summary>
            /// 循环=LOOP
            /// </summary>
            FOR,
            /// <summary>
            /// 屏蔽代码段输出
            /// </summary>
            NOTE,
            /// <summary>
            /// 绑定的数据为true非0非null时输出代码
            /// </summary>
            IF,
            /// <summary>
            /// 绑定的数据为false或者0或者null时输出代码
            /// </summary>
            NOT,
            /// <summary>
            /// 用于标识一个子段模板，可以被别的模板引用
            /// </summary>
            NAME,
            /// <summary>
            /// 引用NAME标识一个子段模板
            /// </summary>
            FROMNAME,
            /// <summary>
            /// 用于标识一个子段程序代码，用于代码的分类输出
            /// </summary>
            PART,
        }
        /// <summary>
        /// 声明与警告+文件头
        /// </summary>
        public const string WarningCode = @"//本文件由程序自动生成,请不要自行修改
using System;
using fastCSharp;

#if NotFastCSharpCode
#else
#pragma warning disable
";
        /// <summary>
        /// 文件结束
        /// </summary>
        public const string FileEndCode = @"
#endif";
        /// <summary>
        /// 已经生成的代码
        /// </summary>
        private static stringBuilder codes = new stringBuilder();
        /// <summary>
        /// 没有依赖的记忆代码
        /// </summary>
        private static stringBuilder rememberCodes = new stringBuilder();
        /// <summary>
        /// 是否存在生成代码
        /// </summary>
        public static bool IsCode
        {
            get { return codes.Count != 0 || rememberCodes.Count != 0; }
        }
        /// <summary>
        /// 已经生成代码的类型
        /// </summary>
        private static Dictionary<Type, HashSet<Type>> codeTypes = dictionary.CreateOnly<Type, HashSet<Type>>();
        /// <summary>
        /// CSharp代码树节点缓存
        /// </summary>
        private static Dictionary<string, treeBuilder.node> nodeCache = dictionary.CreateOnly<string, treeBuilder.node>();
        /// <summary>
        /// 安装参数
        /// </summary>
        private auto.parameter parameter;
        /// <summary>
        /// CSharp代码生成器
        /// </summary>
        /// <param name="parameter">安装参数</param>
        /// <param name="type">模板数据视图</param>
        public coder(auto.parameter parameter, Type type)
            : base(type, error.Add, error.Message)
        {
            this.parameter = parameter;
            creators[command.NOTE.ToString()] = note;
            creators[command.LOOP.ToString()] = creators[command.FOR.ToString()] = loop;
            creators[command.AT.ToString()] = at;
            creators[command.PUSH.ToString()] = push;
            creators[command.IF.ToString()] = ifThen;
            creators[command.NOT.ToString()] = not;
            creators[command.NAME.ToString()] = name;
            creators[command.FROMNAME.ToString()] = fromName;
            creators[command.PART.ToString()] = part;
        }
        /// <summary>
        /// 根据类型名称获取子段模板
        /// </summary>
        /// <param name="typeName">类型名称</param>
        /// <param name="name">子段模板名称</param>
        /// <returns>子段模板</returns>
        protected override treeBuilder.node fromNameNode(string typeName, string name)
        {
            treeBuilder.node node = getNode(typeName);
            if (node != null)
            {
                node = node.GetFirstNodeByTag(command.NAME, name);
                if (node == null) error.Add("模板文件 " + getTemplateFileName(typeName) + " 未找到NAME " + name);
            }
            return node;
        }

        /// <summary>
        /// 创建CSharp代码生成器
        /// </summary>
        /// <param name="type">模板数据视图</param>
        /// <param name="auto">安装属性</param>
        /// <returns>生成器代码</returns>
        private string createClass(Type type, auto auto)
        {
            coder code = new coder(parameter, type);
            code.skin(getNode(auto.GetFileName(type)));
            return code.partCodes["CLASS"];
        }
        /// <summary>
        /// 根据类型名称获取模板文件名
        /// </summary>
        /// <param name="typeName">类型名称</param>
        /// <returns>模板文件名</returns>
        private string getTemplateFileName(string typeName)
        {
            return new DirectoryInfo(parameter.ProjectPath).Parent.fullName() + templatePath + typeName + ".cs";
        }
        /// <summary>
        /// 根据类型名称获取CSharp代码树节点
        /// </summary>
        /// <param name="typeName">类型名称</param>
        /// <returns>CSharp代码树节点</returns>
        private treeBuilder.node getNode(string fileName)
        {
            treeBuilder.node node;
            if (!nodeCache.TryGetValue(fileName, out node))
            {
                fileName = getTemplateFileName(fileName);
                if (File.Exists(fileName))
                {
                    nodeCache.Add(fileName, node = new treeBuilder().create(File.ReadAllText(fileName)));
                }
                else error.Add("未找到模板文件 " + fileName);
            }
            return node;
        }
        /// <summary>
        /// 添加代码
        /// </summary>
        /// <param name="code">代码</param>
        public static void Add(string code)
        {
            codes.Add(code);
        }
        /// <summary>
        /// 添加代码
        /// </summary>
        /// <param name="cSharperType">模板类型</param>
        /// <param name="type">实例类型</param>
        /// <param name="code">代码,null表示锁定</param>
        /// <returns>锁定是否成功</returns>
        public static bool Add(Type cSharperType, Type type, string code)
        {
            if (code == null)
            {
                HashSet<Type> types;
                if (!codeTypes.TryGetValue(cSharperType, out types)) codeTypes.Add(cSharperType, types = hashSet.CreateOnly<Type>());
                if (types.Contains(type)) return false;
                types.Add(type);
            }
            else Add(code);
            return true;
        }
        /// <summary>
        /// 添加没有依赖的记忆代码
        /// </summary>
        /// <param name="code">没有依赖的记忆代码</param>
        public static void AddRemember(string code)
        {
            rememberCodes.Add(code);
        }
        /// <summary>
        /// 输出代码
        /// </summary>
        public static void Output(auto.parameter parameter)
        {
            string[] codeArray = codes.GetArray(), rememberCodeArray = rememberCodes.GetArray();
            codes.Clear();
            codeTypes.Clear();
            rememberCodes.Clear();
            error.ThrowError();
            string code = string.Concat(codeArray), message = string.Empty;
            if (code.length() != 0)
            {
                string fileName = parameter.ProjectPath + "{" + parameter.DefaultNamespace + "}." + pub.fastCSharp + ".cs";
                if (WriteFile(fileName, WarningCode + code + FileEndCode)) message = fileName + " 被修改";
            }
            if ((code = string.Concat(rememberCodeArray)).length() != 0)
            {
                string fileName = parameter.ProjectPath + "{" + parameter.DefaultNamespace + "}.remember." + pub.fastCSharp + ".cs";
                if (WriteFile(fileName, WarningCode + code + FileEndCode)) message += @"
" + fileName + " 被修改";
            }
            if (message.Length != 0) fastCSharp.log.Default.ThrowReal(message, new System.Diagnostics.StackFrame(), false);
        }
        /// <summary>
        /// 输出代码
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="content">文件内容</param>
        /// <returns>是否写入新内容</returns>
        public static bool WriteFile(string fileName, string content)
        {
            try
            {
                if (File.Exists(fileName))
                {
                    if (File.ReadAllText(fileName) != content) return MoveFile(fileName, content);
                }
                else
                {
                    File.WriteAllText(fileName, content);
                    return true;
                }
            }
            catch (Exception error)
            {
                log.Default.ThrowReal(error, "文件创建失败 : " + fileName, false);
            }
            return false;
        }
        /// <summary>
        /// 输出代码
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="content">文件内容</param>
        /// <returns>是否写入新内容</returns>
        public static bool MoveFile(string fileName, string content)
        {
            try
            {
                fastCSharp.io.file.MoveBak(fileName);
                File.WriteAllText(fileName, content);
                return true;
            }
            catch (Exception error)
            {
                log.Default.ThrowReal(error, "文件创建失败 : " + fileName, false);
            }
            return false;
        }

        /// <summary>
        /// CSharp代码生成数据视图生成
        /// </summary>
        [auto(Name = "C#", IsAuto = true, CSharpTemplate = null, isCSharper = false)]
        internal sealed class cSharper : IAuto
        {
            /// <summary>
            /// 类定义生成
            /// </summary>
            private sealed class definition
            {
                /// <summary>
                /// 类型
                /// </summary>
                public Type Type;
                /// <summary>
                /// 安装属性
                /// </summary>
                public auto Auto;
                /// <summary>
                /// 安装参数
                /// </summary>
                public auto.parameter Parameter;
                /// <summary>
                /// 生成类定义字符串
                /// </summary>
                /// <returns>类定义字符串</returns>
                public override string ToString()
                {
                    fastCSharp.code.cSharper.definition definition = new fastCSharp.code.cSharper.definition(Type, true, true);
                    coder code = new coder(Parameter, Type);
                    code.skin(code.getNode(Auto.GetFileName(Type)));
                    stringBuilder codeBuilder = new stringBuilder();
                    codeBuilder.Append(@"
", definition.Start);
//                    foreach (KeyValuePair<string, keyValue<memberIndex, string>> property in code.propertyNames)
//                    {
//                        codeBuilder.Append(@"
//        private bool ", property.Value.Value, @"_;
//        private ", property.Value.Key.Type.fullName(), " ", property.Value.Value, @"v;
//        private ", property.Value.Key.Type.fullName(), " ", property.Value.Value, @"
//        {
//            get
//            {
//                if (!", property.Value.Value, @"_)
//                {
//                    ", property.Value.Value, "v = ", property.Key, @";
//                    ", property.Value.Value, @"_ = true;
//                }
//                return ", property.Value.Value, @"v;
//            }
//        }");
//                    }
                    codeBuilder.Append(@"
        /// <summary>
        /// 生成代码
        /// </summary>
        /// <param name=""isOut"">是否输出代码</param>
        protected override void create(bool isOut)
        {
            fastCSharp.code.cSharper.definition definition = isOut ? outStart() : null;
            if (!isOut || definition != null)
            {
                ", code.partCodes["CLASS"], @"
                outEnd(definition);
            }
        }", definition.End);
                    return codeBuilder.ToString();
                }
            }
            /// <summary>
            /// 安装入口
            /// </summary>
            /// <param name="parameter">安装参数</param>
            /// <returns>是否安装成功</returns>
            public bool Run(auto.parameter parameter)
            {
                if (parameter != null)
                {
                    if (parameter.IsFastCSharp)
                    {
                        string[] codes = ui.CurrentAssembly.GetTypes().getArray(type => new definition { Type = type, Auto = type.customAttribute<auto>(), Parameter = parameter })
                            .getFind(type => type.Auto != null && type.Auto.isCSharper)// && type.Auto.DependType == typeof(cSharper)
                            .Sort((left, right) => string.CompareOrdinal(left.Type.FullName, right.Type.FullName))
                            .GetArray(type => type.ToString());
                        if (!error.IsError)
                        {
                            string fileName = new DirectoryInfo(parameter.ProjectPath).Parent.fullName() + @"ui\{" + pub.fastCSharp + "}.cSharper.cs";
                            if (WriteFile(fileName, WarningCode + string.Concat(codes) + FileEndCode))
                            {
                                error.Add(fileName + " 被修改");
                                throw new Exception();
                            }
                            return true;
                        }
                        return false;
                    }
                    return true;
                }
                return false;
            }
        }
    }
}
